/*
 *Copyright 2021-2023 NERCIS
 *
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *Unless required by applicable law or agreed to in writing, software
 *distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *See the License for the specific language governing permissions and
 *limitations under the License.
 */

package cn.ac.nercis.pes.service.document.common;

import cn.ac.nercis.pes.common.constant.evaluation.EvaluationStage;
import cn.ac.nercis.pes.model.bo.doc.common.evaluation.EvaluationBasisBO;
import cn.ac.nercis.pes.model.bo.doc.common.evaluation.EvaluationProcessBO;
import cn.ac.nercis.pes.model.bo.doc.common.evaluation.EvaluationRangeBO;
import cn.ac.nercis.pes.model.bo.doc.common.table.EvaluatorBO;
import cn.ac.nercis.pes.model.bo.doc.common.table.PasswordServerBO;
import cn.ac.nercis.pes.model.bo.doc.report.evaluation.ToolCheckPointBO;
import cn.ac.nercis.pes.model.bo.doc.report.table.ToolReportBO;
import cn.ac.nercis.pes.model.dto.evaluation.content.EvaluationBasisOptionDTO;
import cn.ac.nercis.pes.model.dto.evaluation.content.ToolCheckPointDTO;
import cn.ac.nercis.pes.model.dto.evaluation.prepare.EvaluationStageDTO;
import cn.ac.nercis.pes.model.dto.evaluation.prepare.EvaluationToolsDTO;
import cn.ac.nercis.pes.model.dto.evaluation.prepare.EvaluatorDTO;
import cn.ac.nercis.pes.model.dto.evaluation.prepare.PasswordServerDTO;
import cn.ac.nercis.pes.repository.dal.evaluation.EvaluationProcessRepository;
import cn.ac.nercis.pes.repository.model.project.SystemDO;
import cn.ac.nercis.pes.service.evaluation.EvaluationContentService;
import cn.ac.nercis.pes.service.evaluation.EvaluationProcessService;
import cn.ac.nercis.pes.service.evaluation.EvaluationToolsService;
import cn.ac.nercis.pes.service.evaluation.PasswordServerService;
import cn.ac.nercis.pes.service.utils.FileManage;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 密评文档通用数据服务类
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public record DocDataService(EvaluationProcessRepository evaluationProcessRepository,
                             EvaluationContentService evaluationContentService,
                             EvaluationProcessService evaluationProcessService,
                             DocTargetDataService docTargetDataService,
                             EvaluationToolsService evaluationToolsService,
                             PasswordServerService passwordServerService) {
    private static final DateTimeFormatter REPORT_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy年MM月");
    private static final DateTimeFormatter LOCAL_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy年MM月dd日");

    @Autowired
    public DocDataService {
    }

    public String getReportDate(@NonNull SystemDO systemDO) {
        var reportPlan = evaluationProcessRepository.findBySystemIdAndName(systemDO.getId(), EvaluationStage.REPORT.getName())
                .stream()
                .findFirst()
                .orElse(null);
        if (Objects.nonNull(reportPlan) && Objects.nonNull(reportPlan.getEndDate())) {
            return REPORT_DATE_FORMATTER.format(reportPlan.getEndDate());
        } else {
            return REPORT_DATE_FORMATTER.format(LocalDate.now());
        }
    }

    public EvaluationBasisBO getEvaluationBasis(@NonNull SystemDO systemDO) {
        try {
            var basis = evaluationContentService.findEvaluationBasis(systemDO.getId());
            var basisList = basis.getBasisStandard()
                    .stream()
                    .filter(EvaluationBasisOptionDTO::getSelected)
                    .map(EvaluationBasisOptionDTO::getName)
                    .toList();
            var refList = basis.getRefStandard()
                    .stream()
                    .filter(EvaluationBasisOptionDTO::getSelected)
                    .map(EvaluationBasisOptionDTO::getName)
                    .toList();
            return EvaluationBasisBO.builder()
                    .basisList(basisList)
                    .refList(refList)
                    .build();
        } catch (RuntimeException rEx) {
            log.error("{}/getEvaluationBasis 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public EvaluationProcessBO getEvaluationProcess(@NonNull SystemDO systemDO) {
        try {
            EvaluationStageDTO stage = evaluationProcessService.getEvaluationStage(systemDO.getId());
            return EvaluationProcessBO.builder()
                    .preparationStartDate(toString(stage.getPreparationStartDate()))
                    .preparationEndDate(toString(stage.getPreparationEndDate()))
                    .compilationStartDate(toString(stage.getCompilationStartDate()))
                    .compilationEndDate(toString(stage.getCompilationEndDate()))
                    .evaluationStartDate(toString(stage.getEvaluationStartDate()))
                    .evaluationEndDate(toString(stage.getEvaluationEndDate()))
                    .analysisStartDate(toString(stage.getAnalysisStartDate()))
                    .analysisEndDate(toString(stage.getAnalysisEndDate()))
                    .build();
        } catch (RuntimeException rEx) {
            log.error("{}/getEvaluationProcess 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public List<EvaluatorBO> getEvaluatorTable(@NonNull SystemDO systemDO) {
        try {
            List<EvaluatorDTO> evaluatorDTOList = this.evaluationProcessService.findEvaluators(systemDO.getId());
            if (CollectionUtils.isEmpty(evaluatorDTOList)) {
                return evaluatorTableByEmpty();
            }
            return evaluatorDTOList.stream()
                    .map(item->EvaluatorBO.builder()
                            .role(item.getRole())
                            .name(item.getName())
                            .desc(item.getDesc())
                            .pass(item.getPassTest())
                            .build())
                    .toList();
        } catch (RuntimeException rEx) {
            log.error("{}/getEvaluatorTable 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public EvaluationRangeBO getEvaluationRange(@NonNull SystemDO systemDO) {
        try {
            ToolCheckPointDTO checkPointDTO = evaluationContentService.findToolCheckPoint(systemDO.getId());
            return EvaluationRangeBO.builder()
                    .baseTarget(docTargetDataService.getBaseTarget(systemDO))
                    .specialTarget(docTargetDataService.getSpecialTarget(systemDO))
                    .notUseTarget(docTargetDataService.getNotUseTarget(systemDO))
                    .toolTable(getToolTable(systemDO.getId()))
                    .toolCheckPoint(ToolCheckPointBO.builder()
                            .pictureDir(FileManage.getCheckPointDir(systemDO.getId()))
                            .checkPointPicture(checkPointDTO.getCheckPointPicture())
                            .desc(checkPointDTO.getDesc())
                            .build())
                    .confirm(docTargetDataService.getAssetEvaluationMethod(systemDO))
                    .build();
        } catch (RuntimeException rEx) {
            log.error("{}/getEvaluationRange 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public List<PasswordServerBO> getPwdServiceTable(String systemId) {
        List<PasswordServerDTO> passwordServers = this.passwordServerService.findPasswordServer(systemId);
        if (CollectionUtils.isEmpty(passwordServers)) {
            return pwdServiceTableByEmpty();
        }
        AtomicInteger number = new AtomicInteger(1);
        return passwordServers.stream()
                .map(item -> PasswordServerBO.builder()
                        .number(String.valueOf(number.getAndIncrement()))
                        .name(item.getName())
                        .vendor(item.getVendor())
                        .build())
                .toList();
    }

    private List<PasswordServerBO> pwdServiceTableByEmpty() {
        return List.of(PasswordServerBO.builder()
                .name("")
                .number("")
                .vendor("")
                .build());
    }

    private String toString(LocalDate localDate) {
        if (Objects.isNull(localDate)) {
            return "";
        }
        return LOCAL_DATE_FORMATTER.format(localDate);
    }

    private List<EvaluatorBO> evaluatorTableByEmpty() {
        return List.of(EvaluatorBO.builder()
                .role("")
                .name("")
                .desc("")
                .pass("")
                .build());
    }

    private List<ToolReportBO> getToolTable(String systemId) {
        List<EvaluationToolsDTO> evaluationTools = this.evaluationToolsService.findEvaluationTools(systemId);
        if (CollectionUtils.isEmpty(evaluationTools)) {
            return toolTableByEmpty();
        }
        AtomicInteger number = new AtomicInteger(1);
        return evaluationTools.stream()
                .map(item -> ToolReportBO.builder()
                        .number(String.valueOf(number.getAndIncrement()))
                        .name(item.getName())
                        .version(item.getVersion())
                        .vendor(item.getVendor())
                        .desc(item.getDesc())
                        .build())
                .toList();
    }

    private List<ToolReportBO> toolTableByEmpty(){
        return List.of(ToolReportBO.builder()
                .number("")
                .name("")
                .version("")
                .vendor("")
                .desc("")
                .build());
    }

}
